package com.shoes.interceptor;

import com.alibaba.fastjson.JSON;
import com.shoes.model.ResponseDO;
import com.shoes.utils.LoggerUtil;
import com.shoes.utils.ResultCode;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.TimeZone;

/**
 * @author asen
 */
public class SignatureInterceptor extends HandlerInterceptorAdapter {
    private static final String AUTHORIZATION = "Authorization";
    private static final String DATE = "Date";
    private static final String HTTPS = "Https";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        boolean result = true;
//        暂时不开放https不做完整性验证
//        if(HTTPS.equalsIgnoreCase(request.getScheme())) {
//            /** 对于Https请求的接口 不做完整性 */
//            LoggerUtil.logger.info("接口请求类型：" + HTTPS);
//            return true;
//        }

        LoggerUtil.logger.info(request.getContextPath() + request.getServletPath() +
                "***********完整性验证**************");

        /** 验证完整性 */
        long start = System.currentTimeMillis();
        result = checkSignature(request);
        long end = System.currentTimeMillis();
        LoggerUtil.logger.info("完整性验证耗时:" + (end - start) + "ms");

        if(!result) {
            LoggerUtil.logger.info("************验证完整性失败**********");
            /** 未通过完整性验证的 操作 (可以对拉黑IP等等)*/
            PrintWriter writer = response.getWriter();
            writer.write(JSON.toJSONString(new ResponseDO(ResultCode.SIGNATURE_CHECK_ERROR, null)));
            writer.flush();
            writer.close();
        }

        return result;
    }

    /**
     * 验证签名
     * 签名生成规则:Utils.md5(请求实体 + "&" + 客户端确定的日期 + "&" + uri(servlet路径) + "&DefaultEncrypt";
     */
    private boolean checkSignature(HttpServletRequest request) {
        boolean result = false;
        String authorization = request.getHeader(AUTHORIZATION);
        String date = request.getHeader(DATE);
        String uri = request.getContextPath() + request.getServletPath();
        uri = uri.replaceAll("//", "/");
        StringBuilder sb = new StringBuilder("");
        Enumeration<String> params = request.getParameterNames();
        while(params.hasMoreElements()) {
            String name = params.nextElement();
            sb.append(name + "=" + request.getParameter(name) + "&");
        }

        if(sb.length() == 0) {//没有参数
            return true;
        }

        String data = sb.deleteCharAt(sb.length() - 1).toString();
        System.out.println(request.getMethod() + " data:" + data);
        /** 服务器生成sign*/
        String sign = md5(data + "&" + date + "&" + uri + "&" + "DefaultEncrypt");
        System.out.println("服务器计算出字串：" + data + "&" + date + "&" + uri + "&" + "DefaultEncrypt");

        SimpleDateFormat format = new SimpleDateFormat(
                "EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        System.out.println("Server GMT:" + format.format(new Date()));
        System.out.println("sign: " + sign);
        System.out.println("Authorization: " + authorization);
        if(sign.equals(authorization)) {
            LoggerUtil.logger.info("************验证完整性成功**********");
            result = true;
        }
        return result ;
    }

    /**
     * 对字符串进行 MD5 加密
     * @param str
     *            待加密字符串
     * @return 加密后字符串
     */
    public static String md5(String str) {
        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'A', 'B', 'C', 'D', 'E', 'F' };
        MessageDigest md5;
        try {
            md5 = MessageDigest.getInstance("MD5");
            md5.update(str.getBytes("UTF-8"));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
        byte[] encodedValue = md5.digest();
        int j = encodedValue.length;
        char finalValue[] = new char[j * 2];
        int k = 0;
        for (int i = 0; i < j; i++) {
            byte encoded = encodedValue[i];
            finalValue[k++] = hexDigits[encoded >> 4 & 0xf];
            finalValue[k++] = hexDigits[encoded & 0xf];
        }

        return new String(finalValue);
    }
}
